/*************************************************************************/
/****	DVI                                                                   ****/
/*************************************************************************/

#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>

#include <stdio.h>
#include <string.h>

#include "dvi_win.h"
#include "dvi.rh"
#pragma warn -par
#pragma warn -ucp


/*************************************************************************/
// Start TOS-Emulation

HANDLE Fcreate( char *name, short attr )
{
#ifdef __16bit
	OFSTRUCT buf;
	HFILE hdl = OpenFile( (LPCSTR)name, &buf, attr );
	if( hdl == HFILE_ERROR ) {
		return ( -buf.nErrCode );
	}
	return ( hdl );
#else
	return ( CreateFile( (LPCSTR)name, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL ) );
#endif
}


HANDLE Fopen( char *name, short attr )
{
#ifdef __16bit
	OFSTRUCT buf;
	HFILE hdl = OpenFile( (LPCSTR)name, &buf, attr );
	if( hdl == HFILE_ERROR ) {
		return ( -buf.nErrCode );
	}
	return ( hdl );
#else
	return ( CreateFile( (LPCSTR)name, GENERIC_READ, FILE_SHARE_WRITE|FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ) );
#endif
}


LONG Fseek( long offset, HANDLE hdl, short mode )
{
//	return _llseek( hdl, offset, mode );
	return ( SetFilePointer( hdl, offset, NULL, mode ) );
}


LONG Fread( HANDLE hdl, long count, void *buf )
{
//	return _hread( hdl, buf, count );
	LONG length;
	ReadFile( hdl, (LPVOID)buf, (DWORD)count, (LPDWORD)&length, NULL );
	return ( length );
}


LONG Fwrite( HANDLE hdl, LONG count, void *buf )
{
//	return _hwrite( hdl, buf, count ); // 16 Bit
	LONG length;
	WriteFile( hdl, (LPVOID)buf, (DWORD)count, (LPDWORD)&length, NULL );
	return ( length );
}


short Fclose( HANDLE hdl )
{
// return _lclose( hdl )-1; // 16 Bit
	return ( (WORD)( TRUE == CloseHandle( hdl ) ) );
}


short Fattrib( char *name, int a, int b )
{
	OFSTRUCT buf;
	if( OpenFile( (LPCSTR)name, &buf, OF_EXIST ) > 0 ) {
		return ( 1 );
	}
	else {
		return ( -33 );
	}
}


// ändert Mausform
HCURSOR mouse_form( LPSTR cursor )
{
	return ( SetCursor( LoadCursor( NULL, cursor ) ) );
}


// Ende TOS-Emulation
/*************************************************************************/


void BusyMenu( DVI_DOC *doc )
{
	extern HWND hwndInfo, hwndToolbar, hwndClient;
	extern HANDLE hMenuDVI;

	if( doc != NULL  &&  doc->status.reading == FALSE ) {
		SendMessage( hwndToolbar, TB_ENABLEBUTTON, MAPREV, ( doc->dseite > 0 ) );
		SendMessage( hwndToolbar, TB_ENABLEBUTTON, MANEXT, ( doc->dseite < doc->limit_seiten-1 ) );
		EnableMenuItem( hMenuDVI, MAPREV, ( doc->dseite == 0 ) );
		EnableMenuItem( hMenuDVI, MANEXT, ( doc->dseite >= doc->limit_seiten ) );
		EnableMenuItem( hMenuDVI, MAGEHE, FALSE );
	}
	else {
		SendMessage( hwndToolbar, TB_ENABLEBUTTON, MAPREV, FALSE );
		SendMessage( hwndToolbar, TB_ENABLEBUTTON, MANEXT, FALSE );
		EnableMenuItem( hMenuDVI, MAPREV, TRUE );
		EnableMenuItem( hMenuDVI, MANEXT, TRUE );
		EnableMenuItem( hMenuDVI, MAGEHE, TRUE );
	}
}


// 11.11.99


// Menueintraege aktuell halten
void UpdateMenu( DVI_DOC *doc )
{
	extern HWND hwndInfo, hwndToolbar, hwndClient;
	extern HANDLE hMenuDVI;

	EnableMenuItem( hMenuDVI, MDCLOSE, doc == NULL );
	if( doc != NULL ) {
		// Enable some entries
		BusyMenu( doc );
		EnableMenuItem( hMenuDVI, MDNEULES, FALSE );
		SendMessage( hwndToolbar, TB_ENABLEBUTTON, MAGROSS, ( doc->status.hfaktor > 1 ) );
		SendMessage( hwndToolbar, TB_ENABLEBUTTON, MAKLEIN, ( doc->status.hfaktor < 8 ) );
		EnableMenuItem( hMenuDVI, MAGROSS, ( doc->status.hfaktor == 1 ) );
		EnableMenuItem( hMenuDVI, MAKLEIN, ( doc->status.hfaktor >= 8 ) );
		EnableMenuItem( hMenuDVI, MAGROSS, ( doc->status.hfaktor == 1 ) );
		SendMessage( hwndToolbar, TB_ENABLEBUTTON, MDSEITE, TRUE );
		EnableMenuItem( hMenuDVI, MDSEITE, FALSE  );
	}
	else {
		// Disanable some entries
		BusyMenu( doc );
		EnableMenuItem( hMenuDVI, MDNEULES, TRUE );
		SendMessage( hwndToolbar, TB_ENABLEBUTTON, MAGROSS, FALSE );
		SendMessage( hwndToolbar, TB_ENABLEBUTTON, MAKLEIN, FALSE );
		EnableMenuItem( hMenuDVI, MAKLEIN, TRUE );
		EnableMenuItem( hMenuDVI, MAGROSS, TRUE );
		EnableMenuItem( hMenuDVI, MDSEITE, TRUE );
		SendMessage( hwndToolbar, TB_ENABLEBUTTON, MDSEITE, FALSE );
		EnableMenuItem( hMenuDVI, MDSEITE, TRUE  );
	}
}


// 12.4.98


// Gibt Rechenzeit an andere ab
VOID YieldApp( BOOL Wait )
{
	extern HWND hwndFrame, hwndClient;
	extern HANDLE hAccel;
	BOOL DoIt = FALSE;
	MSG mesg;

	// Damit die anderen auch noch etwas tun k�nnen
	if( Wait ) {
		DoIt = GetMessage( &mesg, NULL, 0, 0 );
	}
	else {
		DoIt = PeekMessage( &mesg, NULL, 0, 0, PM_REMOVE );
	}

	// Ist denn etwas zu tun?
	if( DoIt ) {
		if(   //!IsDialogMessage( hModeLess, &mesg )  &&
		        !TranslateMDISysAccel( hwndClient, &mesg )  &&
		        !TranslateAccelerator( hwndFrame, hAccel, &mesg ) ) {
			TranslateMessage( &mesg );
			DispatchMessage( &mesg );
		}
	}
}


// 27.9.97


void Warning( short was )
{
	char str[256];
	extern HWND hwndFrame;

	if( USER != DAU ) {
		return;
	}
	if( LoadString( hInst, was, (LPSTR)str, 256 ) ) {
		MessageBox( hwndFrame, (LPSTR)str, "DVI for Windows", /*MB_SETFOREGROUND|*/ MB_ICONEXCLAMATION|MB_OK );
	}
}


short Frage( short was, short def )
{
	char str[256];
	extern HWND hwndFrame;

	if( USER == EX ) {
		return ( 1 );
	}
	if( LoadString( hInst, (WORD)was, str, 256 ) ) {
		return ( (WORD)( MessageBox( hwndFrame, (LPSTR)str, "DVI for Windows",
		                             MB_SETFOREGROUND|MB_TASKMODAL|MB_ICONQUESTION|MB_YESNO|( MB_DEFBUTTON2*def ) ) == ( IDYES+def ) ) );
	}
	return ( 1 );
}


LONG Error( short was, LONG ret )
{
	char str[256];
	if( USER == EX ) {
		return ( ret );
	}
	if( LoadString( hInst, was, str, 256 ) ) {
		MessageBox( NULL, (LPSTR)str, "DVI for Windows", MB_SETFOREGROUND|MB_ICONEXCLAMATION|MB_OK );
	}
	return ( ret );
}


VOID Tos_Fatal( DVI_DOC *doc, LONG i, short was )
{
	char str[256];

	if( USER != EX ) {
		if( LoadString( hInst, (WORD)was, str, 256 ) ) {
			MessageBox( NULL, (LPSTR)str, "DVI for Windows", MB_SETFOREGROUND|MB_ICONEXCLAMATION|MB_OK );
		}
	}
	WriteLN( doc, str );
}


/* 23.12.92 */


/****************************************************************************/
/* Infozeilenverwaltung im Fenster */


VOID add_info( DVI_DOC *doc, UBYTE *str )
{
	if( doc->info == 0L ) {
		return;
	}
	if( doc->info_len == 0 ) {
		memset( doc->info, 0, 128 );
	}
	doc->info_len = min( 120, doc->info_len );
	while( *str > 0  &&  doc->info_len < 120 )
		doc->info[doc->info_len++] = *str++;
	doc->info[doc->info_len] = 0;
}


/* 24.10.93 */


VOID Message( DVI_DOC *doc, UBYTE *str )
{
	add_info( doc, str );
	if( doc->ph > 0 ) {
		Fwrite( doc->ph, strlen( str ), str );
	}
}


VOID WriteLN( DVI_DOC *doc, UBYTE *str )
{
	extern HWND hwndInfo;

	add_info( doc, str );
	if( doc->info > 0L ) {
		SendMessage( hwndInfo, SB_SETTEXT, 0, (LPARAM)doc->info );
	}
	doc->info_len = 0;
	if( doc->ph > 0 ) {
		Fwrite( doc->ph, strlen( str ), str );
		Fwrite( doc->ph, 2, "\xD\xA" );
	}
}


/****************************************************************************
 * L��t von Zeit zu Zeit auch den Rest dran ...
 * (Wird auch als scan_event(i) aufgerufen!)
 */
BOOLEAN LetTheOtherDoSomething( void )
{
	extern HANDLE hAccel;
	extern HWND hwndFrame, hwndClient;
	MSG msg;

	if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) {
		if(  //!IsDialogMessage(hModeLess,&msg)  &&
		        !TranslateMDISysAccel( hwndClient, &msg ) &&
		        !TranslateAccelerator( hwndFrame, hAccel, &msg ) ) {
			TranslateMessage( &msg );
			DispatchMessage( &msg );
		}
	}
	return ( FALSE );   // Nie ein User-Abbruch (jedenfalls nicht hier ...)
}


/* 3.3.97 */


// Keine Jobwindows (Leider!)
short job_window( short attr, UBYTE *name, UBYTE *info, short slidew )
{
	return ( 0 );
}

short is_job_window( VOID )
{
	return ( 0 );
}

VOID end_job_window( VOID )
{
}

short drucke_von_bis( DVI_DOC *doc, LONG start, LONG ende, BOOLEAN double_it, BOOLEAN unsort )
{
	return ( 1 );
}


/****************************************************************************/
// Gamma = 1.9
//BYTE	bColor[8] = { 255, 237, 187, 107, 0 }; // korrekt
BYTE bColor[16] = { 255, 230, 196, 160, 128, 107, 64, 16, 0 };          // dunkler


/**** Neuzeichnen von Fenstern ****/
VOID drawDoc( DVI_DOC *doc )
{
	HDC hdc;
	PAINTSTRUCT ps;
	long i, w, h;
	BITMAPINFO *bi;
	SEITE *ax_page;
	WORD xf = doc->status.hfaktor;
	WORD yf = doc->status.vfaktor, maxf;

	if( doc == NULL || doc->seiten == NULL  ||  doc->seiten[doc->dseite] == NULL  ||  doc->display == NULL ) {
		return; /* Nix zu tun */
	}
	ax_page = doc->seiten[doc->dseite];
	hdc = BeginPaint( doc->wh, &ps );

	// Fenster neu zeichnen, Scrollposition ber�cksichtigen ...
	SetWindowOrgEx( hdc, GetScrollPos( doc->wh, SB_HORZ ), GetScrollPos( doc->wh, SB_VERT ), NULL );

	w = ax_page->weite;
	h = ax_page->hoehe;

	// Bitmap anlegen
	bi = calloc( sizeof( BITMAPINFOHEADER )+sizeof( RGBQUAD )*256, 1 );
	bi->bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
	maxf = yf;
	if( yf > 4 ) {
		maxf = 4;
	}
	bi->bmiHeader.biWidth = w/maxf; //doc->display_weite;
	bi->bmiHeader.biHeight = h/maxf;
	bi->bmiHeader.biPlanes = 1;
	bi->bmiHeader.biBitCount = 8;
	bi->bmiHeader.biCompression = BI_RGB;
	bi->bmiHeader.biClrUsed = 16;
	for( i = 0; i <= 8; i++ ) {
		bi->bmiColors[i].rgbRed = bi->bmiColors[i].rgbGreen = bi->bmiColors[i].rgbBlue = bColor[i];
	}
	for(; i < 16; i++ ) {
		bi->bmiColors[i].rgbRed = bi->bmiColors[i].rgbGreen = bi->bmiColors[i].rgbBlue = 0;
	}
	for(; i < 255; i++ ) {
		bi->bmiColors[i].rgbRed = 255;  // F�r Debug 255: Unbekannte Farben sind ROT!
		bi->bmiColors[i].rgbGreen = bi->bmiColors[i].rgbBlue = 0;
	}
	SetStretchBltMode( hdc, WHITEONBLACK );
	SetBrushOrgEx( hdc, 0, 0, NULL );
	SelectObject( hdc, GetStockObject( BLACK_PEN ) );
	if( maxf == 1 ) {
		bi->bmiHeader.biBitCount = 1;
		//bi->bmiHeader.biWidth = doc->display_weite;
		bi->bmiColors[1].rgbRed = bi->bmiColors[1].rgbGreen = bi->bmiColors[1].rgbBlue = 0;
	}
	StretchDIBits( hdc, 0, 0, w/xf, h/yf,
	               0, 0, w/maxf, h/maxf,
	               (LPSTR)doc->display, (LPBITMAPINFO)bi,
	               DIB_RGB_COLORS, SRCCOPY );
	BitBlt( hdc, w/xf, 0, 32000, 32000, NULL, 0, 0, WHITENESS );
	BitBlt( hdc, 0, h/yf, 32000, 32000, NULL, 0, 0, WHITENESS );
	MoveToEx( hdc, w/xf, 0, NULL );
	LineTo( hdc, w/xf, h/yf );
	LineTo( hdc, 0, h/yf );

	Mfree( bi );
	EndPaint( doc->wh, &ps );
	return;
}


// Neu: Halbt�ne (eher Graustufen) 10.4.98
/* 1.3.97 */


UBYTE copy_16_zu_8[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
UBYTE copy_4_zu_4[4] = { 0, 4, 4, 8 };


// Daten in den Bildschirmzwischenspeicher schieben
void copy_to_display_buffer( DVI_DOC *doc )
{
	long y, w, h, wweite;
	int vf = doc->status.vfaktor;
	UWORD i;
	SEITE *ax_page;
	unsigned char *pDest, *pSrc1, *pSrc2, *pSrc3, *pSrc4;
	int x_dest, x_src;

	if( doc == NULL || doc->seiten == NULL  ||  doc->seiten[doc->dseite] == NULL  ||  doc->seiten[doc->dseite]->bitmap == NULL ) {
		return; /* Nix zu tun */
	}
	ax_page = doc->seiten[doc->dseite];
	doc->page_in_display = doc->dseite;

	// R�nder berechnen
	w = ax_page->weite;
	wweite = ( ( w+15l )/16l )*2l;
	h = ax_page->hoehe;
	// Eventuelle Bitmap l�schen
	if( doc->display != NULL ) {
		free( doc->display );
	}

	switch( vf ) {
	case 1:
		doc->display_weite = ( ( w+31 )/32 )*4;
		doc->display = malloc( doc->display_weite*h  );
		pDest = doc->display;
		pSrc1 = ax_page->bitmap + ( h-1l )*wweite;
		for( y = 0; y < h; y++ ) {
			memcpy( pDest, pSrc1, wweite );
			pDest += doc->display_weite;
			pSrc1 -= wweite;
		}
		break;

	case 2:
		doc->display_weite = ( ( w/2+3l )/4 )*4;
		doc->display = calloc( doc->display_weite*( h/2+1 ), 1 );
		pDest = doc->display;
		pSrc1 = ( (char*)ax_page->bitmap )+( h-1l )*wweite;
		pSrc2 = ( (char*)ax_page->bitmap )+( h-2l )*wweite;
		for( y = 0; y < h-1; y += 2 ) {
			for( x_src = 0, x_dest = 0; x_src < w; ) {
				if( pSrc1[x_src>>3] == 0  &&  pSrc2[x_src>>3] == 0 ) {
					x_src += 8;
					x_dest += 4;
					continue;
				}
				switch( x_src&7 ) {
				case 0:
					i = ( pSrc1[( x_src>>3 )]|pSrc2[( x_src>>3 )] )>>6;
					break;

				case 2:
					i = ( ( pSrc1[( x_src>>3 )]|pSrc2[( x_src>>3 )] )>>4 )&0x03;
					break;

				case 4:
					i = ( ( pSrc1[( x_src>>3 )]|pSrc2[( x_src>>3 )] )>>2 )&0x03;
					break;

				case 6:
					i = ( pSrc1[( x_src>>3 )]|pSrc2[( x_src>>3 )] )&0x03;
					break;
				}
				pDest[x_dest++] = copy_4_zu_4[i];
				x_src += 2;
			}
			pDest += doc->display_weite;
			pSrc1 -= 2*wweite;
			pSrc2 -= 2*wweite;
		}
		break;

	case 3:
	{
		LONG wert1, wert2, wert3;
		LONG i, j;

		doc->display_weite = ( ( w/3+3l )/4 )*4;
		doc->display = calloc( doc->display_weite*( h/3+1 ), 1 );
		pDest = doc->display;
		pSrc1 = ( (char*)ax_page->bitmap )+( h-1l )*wweite;
		pSrc2 = ( (char*)ax_page->bitmap )+( h-2l )*wweite;
		pSrc3 = ( (char*)ax_page->bitmap )+( h-3l )*wweite;
		for( y = 0; y < h-2; y += 3 ) {
			for( x_src = 0, x_dest = 0; x_src < w; ) {
				i = x_src>>3;
				x_src += 24;
				if( w < x_src ) {
					break;
				}

				wert1 = ( (ULONG)pSrc1[i]<<16 ) | ( (ULONG)pSrc1[i+1]<<8 ) | (ULONG)pSrc1[i+2];
				wert2 = ( (ULONG)pSrc2[i]<<16 ) | ( (ULONG)pSrc2[i+1]<<8 ) | (ULONG)pSrc2[i+2];
				wert3 = ( (ULONG)pSrc3[i]<<16 ) | ( (ULONG)pSrc3[i+1]<<8 ) | (ULONG)pSrc3[i+2];

				if( ( wert1|wert2|wert3 ) != 0 ) {
					for( i = 21; i >= 0; i -= 3 ) {
						j = copy_16_zu_8[( wert1>>i )&0x07]+copy_16_zu_8[( wert2>>i )&0x07]+copy_16_zu_8[( wert3>>i )&0x07];
						j = ( j*8 )/9;
						if( i&1 ) {
							pDest[x_dest++] = (UBYTE)j;
						}
						else {
							pDest[x_dest++] = (UBYTE)j;
						}
					}
				}
				else {
					x_dest += 8;
				}
			}
			pDest += doc->display_weite;
			pSrc1 -= 3*wweite;
			pSrc2 -= 3*wweite;
			pSrc3 -= 3*wweite;
		}
		break;
	}

	case 4:
	default:
		doc->display_weite = ( ( w/4+3l )/4 )*4;
		doc->display = calloc( doc->display_weite*( h/4+1 ), 1 );
		pDest = doc->display;
		pSrc1 = ( (char*)ax_page->bitmap )+( h-1l )*wweite;
		pSrc2 = ( (char*)ax_page->bitmap )+( h-2l )*wweite;
		pSrc3 = ( (char*)ax_page->bitmap )+( h-3l )*wweite;
		pSrc4 = ( (char*)ax_page->bitmap )+( h-4l )*wweite;
		for( y = 0; y < h-3; y += 4 ) {
			for( x_src = 0, x_dest = 0; x_src < w/8; x_src++ ) {
#if 0
				// "Verd�nnt" die Farben zu sehr
				i =  copy_16_zu_8[(UWORD)pSrc1[x_src]>>4]+
				    copy_16_zu_8[(UWORD)pSrc2[x_src]>>4]+
				    copy_16_zu_8[(UWORD)pSrc3[x_src]>>4]+
				    copy_16_zu_8[(UWORD)pSrc4[x_src]>>4];
				pDest[x_dest++] = i/4;
				i =  copy_16_zu_8[(UWORD)pSrc1[x_src]&0x0F]+
				    copy_16_zu_8[(UWORD)pSrc2[x_src]&0x0F]+
				    copy_16_zu_8[(UWORD)pSrc3[x_src]&0x0F]+
				    copy_16_zu_8[(UWORD)pSrc4[x_src]&0x0F];
				pDest[x_dest++] = i/4;
#endif
#if 1
				// beste Mischung aus zulaufen (durch Oder) und Pixelwerten (durch Addition)
				i =  copy_16_zu_8[(UWORD)( pSrc1[x_src]|pSrc2[x_src] )>>4]+
				    copy_16_zu_8[(UWORD)( pSrc3[x_src]|pSrc4[x_src] )>>4];
				pDest[x_dest++] = (UCHAR)i;
				i =  copy_16_zu_8[(UWORD)( pSrc1[x_src]|pSrc2[x_src] )&0x0F]+
				    copy_16_zu_8[(UWORD)( pSrc3[x_src]|pSrc4[x_src] )&0x0F];
				pDest[x_dest++] = (UCHAR)i;
#endif
#if 0
				// Verdickt die Farben zu sehr
				i =  (UWORD)( pSrc1[x_src]|pSrc2[x_src]|pSrc3[x_src]|pSrc4[x_src] );
				pDest[x_dest++] = copy_16_zu_8[i>>4]*2;
				pDest[x_dest++] = copy_16_zu_8[i&0x0f]*2;
#endif
			}
			pDest += doc->display_weite;
			pSrc1 -= 4*wweite;
			pSrc2 -= 4*wweite;
			pSrc3 -= 4*wweite;
			pSrc4 -= 4*wweite;
		}
		break;
	}
}


// Neu: Halbt�ne
// 10.4.98


/**** Neuzeichnen von Fenstern auf dem Drucker ****/
VOID PrintDocPage( HDC hdc, DVI_DOC *doc, double faktor )
{
	long i, w, h, old_v;
	BITMAPINFO *bi;

	if( doc == NULL || doc->seiten == NULL  ||  doc->seiten[doc->dseite] == NULL  ||  doc->display == NULL ) {
		return; /* Nix zu tun */
	}
	w = doc->seiten[doc->dseite]->weite;
	h = doc->seiten[doc->dseite]->hoehe;

	// Bitmap anlegen
	bi = Malloc( sizeof( BITMAPINFOHEADER )+sizeof( RGBQUAD )*16 );
	bi->bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
	bi->bmiHeader.biBitCount = 1;
	bi->bmiHeader.biWidth = w;
	bi->bmiHeader.biHeight = h;
	bi->bmiHeader.biPlanes = 1;
	bi->bmiHeader.biCompression = BI_RGB;
	bi->bmiHeader.biClrUsed = 8;
	for( i = 0; i < 8; i++ ) {
		bi->bmiColors[i].rgbRed = bi->bmiColors[i].rgbGreen = bi->bmiColors[i].rgbBlue = ( 255>>i );
	}
	old_v = doc->status.vfaktor;
	if( old_v != 1 ) {
		doc->status.vfaktor = 1;
		copy_to_display_buffer( doc );
	}
	StretchDIBits( hdc, 0, 0, w*faktor, h*faktor,
	               0, 0, w, h,
	               (LPSTR)doc->display,
	               (LPBITMAPINFO)bi,
	               DIB_RGB_COLORS, SRCCOPY );
	if( old_v != doc->status.vfaktor ) {
		doc->status.vfaktor = old_v;
		copy_to_display_buffer( doc );
	}
	Mfree( bi );
	return;
}


/* 16.4.97 */


